/**
 * @author haixuanwo
 * @email haixuanwo_clark@hotmail.com
 * @date 2021-01-23
 * @brief libevent 封装类
 */

#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#ifdef EVENT__HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#include "myLibevent.h"

#define PORT 9995
#define MESSAGE "hello I'm haixuanwo\n"

MyLibevent::MyLibevent()
{
	struct sockaddr_in sin;

	// 初始化 event base库
	eventBase = event_base_new();

	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);

	// 绑定listen回调
	listener = evconnlistener_new_bind(eventBase, listener_cb, (void *)eventBase,
		LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
		(struct sockaddr*)&sin,
		sizeof(sin));

	if (!listener)
	{
		fprintf(stderr, "Could not create a listener!\n");
		exit(1);
	}

	// 添加信号事件
	signal_event = evsignal_new(eventBase, SIGINT, signal_cb, (void *)eventBase);
	if (!signal_event || event_add(signal_event, NULL)<0)
	{
		fprintf(stderr, "Could not create/add a signal event!\n");
		exit(1);
	}
}

void MyLibevent::timeout_cb(evutil_socket_t fd, short event, void *arg)
{
	struct timeval newtime;

	// 修改超时时间
	// struct event *timeout = (struct event *)arg;
	// event_add(timeout, &tv);
	
	evutil_gettimeofday(&newtime, NULL);
	printf("timeout_cb called at %d seconds\n", (int)newtime.tv_sec);
}

int MyLibevent::timer_test()
{
	// 初始化一个事件
	event_assign(&timeout, eventBase, -1, EV_PERSIST, timeout_cb, (void*) &timeout);

	// 添加一个事件
	evutil_timerclear(&tv);
	tv.tv_sec = 2;
	event_add(&timeout, &tv);
}

void MyLibevent::listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    	struct sockaddr *sa, int socklen, void *user_data)
{
	struct event_base *base = (struct event_base *)user_data;
	struct bufferevent *bev;

	bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
	if (!bev)
	{
		fprintf(stderr, "Error constructing bufferevent!");
		event_base_loopbreak(base);
		return;
	}
	
	bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
	bufferevent_enable(bev, EV_WRITE);
	bufferevent_disable(bev, EV_READ);
	
	bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}

void MyLibevent::signal_cb(evutil_socket_t sig, short events, void *user_data)
{
	struct event_base *base = (struct event_base *)user_data;
	struct timeval delay = { 2, 0 };

	printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
	event_base_loopexit(base, &delay);
}

void MyLibevent::conn_writecb(struct bufferevent *bev, void *user_data) 
{
	struct evbuffer *output = bufferevent_get_output(bev);

	if (evbuffer_get_length(output) == 0) {
		printf("flushed answer\n");
		bufferevent_free(bev);
	}
}

void MyLibevent::conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
	if (events & BEV_EVENT_EOF)
	{
		printf("Connection closed.\n");
	}
	else if (events & BEV_EVENT_ERROR)
	{
		printf("Got an error on the connection: %s\n", strerror(errno));
	}
	printf("conn_eventcb\n");
	bufferevent_free(bev);
}

int MyLibevent::my_event_base_dispatch()
{
	event_base_dispatch(eventBase);
}

MyLibevent::~MyLibevent()
{
	evconnlistener_free(listener);
	event_free(signal_event);
	event_base_free(eventBase);
}

